﻿
'suppose the minimal disk sector is 1 byte, so do not waste storage space
'do not support the size / clusterId is large than maxInt64
Public Class cluster
    Public Const INVALID As SByte = -1
    Public Shared ReadOnly CHECKSUM As bytes = Nothing
    Public Const CLUSTERID_SIZE As Byte = 8
    Public Const USED_SIZE As Byte = 8
    Public Const LENGTH_SIZE As Byte = 8
    Public Const NEXTCLUSTERID_SIZE As Byte = 8
    Public Const PREVCLUSTERID_SIZE As Byte = 8
    Public Const CHECKSUM_SIZE As Byte = 8
    Public Const CLUSTER_STRUCT_SIZE As Int32 = _
        CLUSTERID_SIZE + USED_SIZE + LENGTH_SIZE + NEXTCLUSTERID_SIZE + PREVCLUSTERID_SIZE + CHECKSUM_SIZE
    Public clusterId As Int64 = INVALID
    Public diskoffset As Int64 = INVALID
    Private _used As Int64 = INVALID
    Public length As Int64 = INVALID
    Public prevclusterId As Int64 = INVALID
    Public nextclusterId As Int64 = INVALID

    Shared Sub New()
        assert(convertor.tobytes(maxInt64, CHECKSUM))
        assert(CHECKSUM.size() = CHECKSUM_SIZE)
    End Sub

    Public Sub New()
    End Sub

    Public Function free() As Boolean
        Return used() = INVALID
    End Function

    Public Function usedSize() As Int64
        Return used()
    End Function

    Private Function used() As Int64
        assert((_used = INVALID OrElse _used >= 0) AndAlso _used <= length)
        Return _used
    End Function

    Private Function readChecksum(ByVal vd As virtdisk, ByRef start As Int64) As Boolean
        Dim bts As bytes = Nothing
        If vd.read(start, CHECKSUM_SIZE, bts) AndAlso _
           bts = CHECKSUM Then
            start += CHECKSUM_SIZE
            Return True
        Else
            Return False
        End If
    End Function

    Private Shared Function readInt64(ByVal vd As virtdisk, ByRef start As Int64, _
                                      ByVal len As SByte, ByRef o As Int64) As Boolean
        Dim bts As bytes = Nothing
        If vd.read(start, len, bts) AndAlso _
           convertor.toint64(bts, o) Then
            start += len
            Return True
        Else
            Return False
        End If
    End Function

    Public Function initial(ByVal vd As virtdisk, ByRef start As Int64) As Boolean
        If vd Is Nothing OrElse Not vd.valid() OrElse start < 0 Then
            Return False
        End If
        diskoffset = start
        If readInt64(vd, start, CLUSTERID_SIZE, clusterId) AndAlso _
           readInt64(vd, start, USED_SIZE, _used) AndAlso _
           readInt64(vd, start, LENGTH_SIZE, length) AndAlso _
           readInt64(vd, start, PREVCLUSTERID_SIZE, prevclusterId) AndAlso _
           readInt64(vd, start, NEXTCLUSTERID_SIZE, nextclusterId) AndAlso _
           readChecksum(vd, start) Then
            'jump the data
            start += length
            Return True
        Else
            Return False
        End If
    End Function

    Public Function read(ByVal vd As virtdisk, ByRef bts As bytes) As Boolean
        If vd Is Nothing OrElse Not vd.valid() Then
            Return False
        End If
        If isdebugmode() Then
            assert(Not free())
        End If
        Return vd.read(diskoffset + CLUSTER_STRUCT_SIZE, used(), bts)
    End Function

    Private Function writeChecksum(ByVal vd As virtdisk, ByRef start As Int64) As Boolean
        If vd.write(start, CHECKSUM) Then
            start += CHECKSUM_SIZE
            Return True
        Else
            Return False
        End If
    End Function

    Private Shared Function writeInt64(ByVal vd As virtdisk, ByRef start As Int64, _
                                       ByVal len As Int64, ByVal i As Int64) As Boolean
        Dim tmp As bytes = Nothing
        If convertor.tobytes(i, tmp) AndAlso _
           vd.write(start, tmp) Then
            If isdebugmode() Then
                assert(tmp.size() = len)
            End If
            start += len
            Return True
        Else
            Return False
        End If
    End Function

    Private Function write_cluster_structure(ByVal vd As virtdisk, Optional ByRef start As Int64 = 0) As Boolean
        start = diskoffset
        Return writeInt64(vd, start, CLUSTERID_SIZE, clusterId) AndAlso _
               writeInt64(vd, start, USED_SIZE, used()) AndAlso _
               writeInt64(vd, start, LENGTH_SIZE, length) AndAlso _
               writeInt64(vd, start, PREVCLUSTERID_SIZE, prevclusterId) AndAlso _
               writeInt64(vd, start, NEXTCLUSTERID_SIZE, nextclusterId) AndAlso _
               writeChecksum(vd, start)
    End Function

    Public Function delete(ByVal vd As virtdisk, Optional ByRef start As Int64 = 0) As Boolean
        If vd Is Nothing OrElse Not vd.valid() Then
            Return False
        End If

        Return eva(_used, INVALID) AndAlso write_cluster_structure(vd, start) AndAlso _
               vd.fill(start, length) AndAlso inc(start, length)
    End Function

    Public Function write(ByVal vd As virtdisk, ByVal bts As bytes, Optional ByRef start As Int64 = 0) As Boolean
        If vd Is Nothing OrElse Not vd.valid() OrElse bts Is Nothing OrElse bts.size > length Then
            Return False
        End If
        'set used, write cluster structure
        'write data
        'if used <> length, fill the following data
        Return eva(_used, bts.size()) AndAlso write_cluster_structure(vd, start) AndAlso _
               vd.write(start, bts) AndAlso inc(start, used()) AndAlso _
               (used() = length OrElse (vd.fill(start, length - used()) AndAlso inc(start, length - used())))
    End Function

    Public Function append(ByVal vd As virtdisk, ByVal bts As bytes) As Boolean
        If free() Then
            Return write(vd, bts)
        ElseIf vd Is Nothing OrElse Not vd.valid() OrElse bts Is Nothing OrElse bts.size > length - used() Then
            Return False
        Else
            'write data
            'increase used
            'write cluster structure
            Return vd.write(diskoffset + CLUSTER_STRUCT_SIZE + used(), bts) AndAlso _
                   inc(_used, bts.size()) AndAlso write_cluster_structure(vd)
        End If
    End Function
End Class
